﻿using GMall.AdministrationService.Menus.Dtos;
using GMall.AdministrationService.Permissions.Dtos;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Users;

namespace GMall.AdministrationService.Menus
{
    public class MenuAppService : AdministrationServiceAppService, IMenuAppService
    {
        private readonly IRepository<PermissionGrant, Guid> _permissionGrantRepository;
        private readonly IRepository<Menu, Guid> _menuRepository;
        private readonly MenuManager _menuManager;
        private readonly IdentityUserManager _identityUserManager;
        public MenuAppService(IRepository<Menu, Guid> menuRepository,
            MenuManager menuManager,
            IRepository<PermissionGrant, Guid> permissionGrantRepository,
            IdentityUserManager identityUserManager)
        {
            _menuRepository = menuRepository;
            _menuManager = menuManager;
            _permissionGrantRepository = permissionGrantRepository;
            _identityUserManager = identityUserManager;

        }

        public virtual async Task<MenuDto> CreateAsync(MenuCreateInput input)
        {
            var menu = new Menu(GuidGenerator.Create(), input.Name, input.DisplayName, input.Url, input.Api, input.Icon, input.Order, input.Type, input.IsActive, input.IsVisible, input.ParentId, input.CustomData);
            await _menuManager.CreateAsync(menu);
            await CurrentUnitOfWork.SaveChangesAsync();
            return ObjectMapper.Map<Menu, MenuDto>(menu);
        }

        public virtual async Task DeleteAsync(Guid id)
        {
            await _menuManager.DeleteAsync(id);
            await CurrentUnitOfWork.SaveChangesAsync();
        }

        public virtual async Task<MenuDto> UpdateAsync(Guid id, MenuUpdateInput input)
        {
            var menu = await _menuRepository.GetAsync(id);
            menu.Update(input.Name, input.DisplayName, input.Url, input.Api, input.Icon, input.Order, input.Type, input.IsActive, input.IsVisible, input.CustomData);
            await _menuRepository.UpdateAsync(menu);
            await CurrentUnitOfWork.SaveChangesAsync();
            return ObjectMapper.Map<Menu, MenuDto>(menu);
        }

        public virtual async Task MoveAsync(MenuMoveInput input)
        {
            await _menuManager.MoveAsync(input.Id, input.ParentId);
            await CurrentUnitOfWork.SaveChangesAsync();
        }

        public virtual async Task<MenuDto> GetAsync(Guid id)
        {
            var entity = await _menuManager.GetByIdAsync(id);
            return ObjectMapper.Map<Menu, MenuDto>(entity);
        }

        public virtual async Task<PagedResultDto<MenuDto>> GetListAsync(MenuGetListInput input)
        {
            var queryable = (await _menuRepository.GetQueryableAsync())
                .WhereIf(input.ParentId.HasValue, m => m.ParentId == input.ParentId)
                .WhereIf(!string.IsNullOrWhiteSpace(input.Code), m => m.Code.Contains(input.Code))
                .WhereIf(!string.IsNullOrWhiteSpace(input.Keyword),
                    m =>
                    m.Name.Contains(input.Keyword) ||
                    m.DisplayName.Contains(input.Keyword) ||
                    m.Code.Contains(input.Keyword)
                );
            // 总数
            var count = await AsyncExecuter.CountAsync(queryable);
            // 排序
            if (!string.IsNullOrWhiteSpace(input.Sorting))
            {
                var isDesc = input.Sorting.EndsWith("_desc");
                input.Sorting = isDesc ? input.Sorting[..^5] : input.Sorting;
                queryable = input.Sorting switch
                {
                    "Order" => isDesc ? queryable.OrderByDescending(m => m.Order) : queryable.OrderBy(m => m.Order),
                    "Code" => isDesc ? queryable.OrderByDescending(m => m.Code) : queryable.OrderBy(m => m.Code),
                    "CreationTime" => isDesc ? queryable.OrderByDescending(m => m.CreationTime) : queryable.OrderBy(m => m.CreationTime),
                    _ => isDesc ? queryable.OrderByDescending(m => m.CreationTime) : queryable.OrderBy(m => m.CreationTime),
                };
            }
            // 分页
            queryable = queryable.PageBy(input.SkipCount, input.MaxResultCount);
            // 列表
            var list = await AsyncExecuter.ToListAsync(queryable);

            return new PagedResultDto<MenuDto>(
                count,
                ObjectMapper.Map<List<Menu>, List<MenuDto>>(list)
            );
        }

        public virtual async Task<TreeMenu> GetTreeMenuAsync(TreeMenuGetInput input)
        {
            var tree = new TreeMenu("Menu", "导航菜单");
            var allMenu = await _menuRepository.GetListAsync();
            var parentMenu = allMenu.FindAll(m => input.Id.HasValue ? m.Id == input.Id.Value : m.ParentId == null);
            foreach (var parent in parentMenu)
            {
                var parentTree = new TreeMenuItem(parent.Id, parent.Name, parent.DisplayName, parent.Code, parent.Url, parent.Api, parent.Icon, parent.Order, parent.Type, parent.IsVisible, parent.ParentId, parent.CustomData);
                var parentAllChildrenMenu = allMenu.FindAll(m => m.Code.StartsWith(parent.Code) && m.Id != parent.Id);
                foreach (var child in parentAllChildrenMenu)
                {
                    var childTree = new TreeMenuItem(child.Id, child.Name, child.DisplayName, child.Code, child.Url, child.Api, child.Icon, child.Order, child.Type, child.IsVisible, child.ParentId, child.CustomData);
                    var childNextChildrenMenu = allMenu.FindAll(m => m.ParentId == child.Id);
                    foreach (var child2 in childNextChildrenMenu)
                    {
                        var childTree2 = new TreeMenuItem(child2.Id, child2.Name, child2.DisplayName, child2.Code, child2.Url, child2.Api, child2.Icon, child2.Order, child2.Type, child2.IsVisible, child2.ParentId, child2.CustomData);
                        childTree.AddItem(childTree2);
                    }
                    if (child.ParentId == parent.Id)
                    {
                        parentTree.AddItem(childTree);
                    }
                }
                tree.AddItem(parentTree);
            }
            return tree;
        }

        public virtual async Task<TreeMenu> GetCurrentTreeMenuAsync()
        {
            var currentUser = await _identityUserManager.GetByIdAsync(CurrentUser.Id.Value);
            var currentRoleIds = currentUser.Roles.Select(u => u.RoleId.ToString()).ToList();
            var grantPermissions = await _permissionGrantRepository.GetListAsync(p => p.ProviderName == "R" && currentRoleIds.Contains(p.ProviderKey));
            var grantMenuIds = grantPermissions.Select(p =>Guid.Parse(p.Name)).ToList();
            var grantMenus = (await _menuRepository.GetQueryableAsync())
                .Where(m => grantMenuIds.Contains(m.Id) && m.IsActive)
                .OrderBy(m=>m.Code)
                .ToList();

            var tree = new TreeMenu("Menu", "导航菜单");
            var parentMenus = grantMenus.FindAll(m => m.Code.Length== grantMenus.FirstOrDefault().Code.Length);
            foreach (var parent in parentMenus)
            {
                var parentTree = new TreeMenuItem(parent.Id, parent.Name, parent.DisplayName, parent.Code, parent.Url, parent.Api, parent.Icon, parent.Order, parent.Type, parent.IsVisible, parent.ParentId, parent.CustomData);
                var childMenus = grantMenus.FindAll(m => m.Code.StartsWith(parent.Code) && m.Id != parent.Id);
                foreach (var child in childMenus)
                {
                    var childTree = new TreeMenuItem(child.Id, child.Name, child.DisplayName, child.Code, child.Url, child.Api, child.Icon, child.Order, child.Type, child.IsVisible, child.ParentId, child.CustomData);
                    var childMenus_next = grantMenus.FindAll(m => m.ParentId == child.Id);
                    foreach (var child2 in childMenus_next)
                    {
                        var childTree2 = new TreeMenuItem(child2.Id, child2.Name, child2.DisplayName, child2.Code, child2.Url, child2.Api, child2.Icon, child2.Order, child2.Type, child2.IsVisible, child2.ParentId, child2.CustomData);
                        childTree.AddItem(childTree2);
                    }
                    if (child.ParentId == parent.Id)
                    {
                        parentTree.AddItem(childTree);
                    }
                }
                tree.AddItem(parentTree);
            }
            return tree;
        }
    }
}
